//var httpPath = "http://q37f783146.qicp.vip/fengquan/"
var httpPath = "http://localhost:8092/fengquan/"
var asdsafdas = '41c43513bef406b4778ce97743100b3d'//文件
//数据库对象
class indexdb {
    constructor(id) {
        this.id = id
        //打开数据库连接,单个文件为一个数据库故id为数据名
        this.request = window.indexedDB.open(id, '1.0');
    }
    openDatabase(callBack) {
        this.request.onupgradeneeded = function (event) {
            //创建表
            event.target.result.createObjectStore('fileData', { keyPath: 'md5' })
        }
        this.request.onsuccess = function (event) {
            callBack(event.target.result)
        }
        this.request.onerror = function (event) {
            console.log('数据库打开报错');
        };
    }
    addObject(data) {
        //碎片md5为数据表主键，data为碎片内容
        this.openDatabase((db) => {
            db.transaction(['fileData'], 'readwrite').objectStore('fileData').add(data);
        })
    }
    getObject(md5) {
        return new Promise((resolve, reject) => {

            this.openDatabase((db) => {
                var transaction = db.transaction(['fileData']);
                var objectStore = transaction.objectStore('fileData');
                var request = objectStore.get(md5);
                request.onerror = function (event) {
                    resolve(false);
                };

                request.onsuccess = function (event) {
                    if (request.result) {
                        resolve(request.result);
                    } else {
                        resolve(false);
                    }
                };
            })
        })
    }
    removeObject() {
        window.indexedDB.deleteDatabase(this.id);
    }

}


class State {
    constructor(item,updataFunction) {
        //对象属性劫持方法
        let isOpenObserver = {
            get: (target, key) => {
                return target[key];
            },
            set: (target, key, value) => {
                this.setOpen(value);
                target[key] = value;
                return true;
            }
        }
        //变大变小数据劫持
        this.isOpen = new Proxy({ isOpen: true }, isOpenObserver);
        //主要数据
        this.state = []
        //两个主体
        this.cudUpdataBox = null
        this.cudUpdataBoxSmall = null
        this.updataFunction=updataFunction
        //调用初始化方法
        this._initUpdata(item)
    }
    setOpen(open) {
        if (open) {
            this.cudUpdataBox.style.display = 'block'
            this.cudUpdataBoxSmall.style.display = 'none'
        } else {
            this.cudUpdataBox.style.display = 'none'
            this.cudUpdataBoxSmall.style.display = 'flex'
        }
    }
    _initUpdata(item) {
        var body = document.getElementsByTagName('body')[0];
        let strNode = `
        <div id="cudUpdataBoxSmall">
            <div class="wrapper">
                <div class="wave"></div>
            </div>
            <span class="qiepian">切片上传中...</span>
        </div>
        <div id="cudUpdataBox">
            <div class="updataHeader">
                <span>切片上传中....</span>
                <span class="close">
                    <span class="iconfont icon-zuixiaohua"></span>
                    <span class="iconfont icon-guanbi"></span>
                </span>
            </div>
            <div class="updatatitle">
                <div class="updataItemName">文件名</div>
                <div class="updataItemProgress">进度</div>
            </div>
            <div class="updataItem">
                <div class="updataItemName">
                    <span class="iconfont icon-FileSlicing-${item.type} updataItemNameIcon"></span>
                    <span class="updataItemNameText">${item.fileName}</span>
                </div>
            <div class="updataItemProgress">
                <div class="second-bar-box">
                    <main class="perspective zjmccNum${0}" id="second-bar">
                        <section class="bar">
                            <div class="bar-face back percentage"></div>
                            <div class="bar-face floor percentage"></div>
                            <div class="bar-face roof percentage"></div>
                            <div class="bar-face front percentage"></div>
                            <div class="indicator">0%</div>
                        </section>
                        <input class="bar-input" type="range" min="0" max="100" value="0" />
                    </main>
                </div>
                <div class="second-bar-box-init">
                    <span class="load">
                        <span>文</span>
                        <span>件</span>
                        <span>初</span>
                        <span>始</span>
                        <span>化</span>
                        <span>...</span>
                    </span>
                </div>
                <span class="bottom stop">暂停</span>
                <span class="bottom continue">继续</span>
            </div>
      </div>
        `
        //添加节点
        body.insertAdjacentHTML('afterbegin', strNode)
        //获取大小两个对象
        this.cudUpdataBox = document.getElementById('cudUpdataBox');
        this.cudUpdataBoxSmall = document.getElementById('cudUpdataBoxSmall');
        //大小对象显示
        this.cudUpdataBox.getElementsByClassName('close')[0].onclick = () => {
            this.isOpen.isOpen = !this.isOpen.isOpen
        }
        this.cudUpdataBoxSmall.onclick = () => {
            this.isOpen.isOpen = !this.isOpen.isOpen
        }
        //注册暂停继续方法
        this.stop();
        //推入数据
        this.state.push(item)
        //更改进度条
        this.getAllInputs(0, 0)
    };

    setState(data) {
        this.state = data;
        return true;
    };
    addState(item) {
        let strNode = `
        <div class="updataItem">
            <div class="updataItemName">
            <span class="iconfont icon-FileSlicing-${item.type} updataItemNameIcon"></span>
                <span class="updataItemNameText">${item.fileName}</span>
            </div>
            <div class="updataItemProgress">
                <div class="second-bar-box">
                    <main class="perspective zjmccNum${this.state.length}" id="second-bar">
                        <section class="bar">
                            <div class="bar-face back percentage"></div>
                            <div class="bar-face floor percentage"></div>
                            <div class="bar-face roof percentage"></div>
                            <div class="bar-face front percentage"></div>
                            <div class="indicator">0%</div>
                        </section>
                        <input class="bar-input" type="range" min="0" max="100" value="0" />
                    </main>
                </div>
                <div class="second-bar-box-init">
                    <span class="load">
                        <span>文</span>
                        <span>件</span>
                        <span>初</span>
                        <span>始</span>
                        <span>化</span>
                        <span>...</span>
                    </span>
                </div>
                <span class="bottom stop">暂停</span>
                <span class="bottom continue">继续</span>
            </div>
        </div>
        `
        //返回index
        let addNode = null
        this.state.map((stateItem, stateIndex) => {
            if (stateItem.file === item.file) {
                addNode = stateIndex
            }
        })
        if (addNode) {
            return addNode;
        } else {
            //添加节点
            this.cudUpdataBox.insertAdjacentHTML('beforeend', strNode)
            //注册暂停继续方法
            this.stop();
            //添加数据绑定
            this.state.push(item);
            return this.state.length - 1;
        }
    };
    stop() {
        let stopArry = this.cudUpdataBox.getElementsByClassName('stop');
        let continueArry = this.cudUpdataBox.getElementsByClassName('continue');
        for (let stopArryIndex = 0; stopArryIndex < stopArry.length; stopArryIndex++) {
            stopArry[stopArryIndex].onclick = () => {
                this.state[stopArryIndex].state = false
            }
            continueArry[stopArryIndex].onclick = () => {
                this.state[stopArryIndex].state = true;
                //添加参数
                this.updataFunction(httpPath + 'file/update', httpPath + 'file/download', httpPath + 'file/fileInfo', this.state[stopArryIndex].file)
            }
        }
    }
    getState() {
        return state;
    };
    updataState(item, index, callback) {
        let updataItem = this.cudUpdataBox.getElementsByClassName('updataItem')[index];
        updataItem.children[1].children[0].style.display = 'flex'
        updataItem.children[1].children[1].style.display = 'none'
        let stetaShangeKeyList = []
        for (const key in this.state[index]) {
            if (this.state[index][key] !== item[key]) {
                stetaShangeKeyList.push(key)
            }
        }
        stetaShangeKeyList.map((keyItem) => {
            switch (keyItem) {
                case 'fileName':
                    updataItem.children[0].children[1].textContent = item.fileName
                    break;
                case 'progress':
                    if (item.progress === 100) {
                        let thisItem=updataItem.children[1];
                        updataItem.children[1].insertAdjacentHTML('beforeend', `<span class="bottom">下载此文件</span><span class="bottom delete">删除此项</span>`);
                        updataItem.children[1].children[4].onclick = function () {
                            callback(this)
                        }
                        updataItem.children[1].children[5].onclick = function () {
                            this.removeState(index)
                        }
                        thisItem.removeChild(thisItem.children[2])
                        thisItem.removeChild(thisItem.children[2])
                    }
                    break;
            }
        })
        //更改进度条
        this.getAllInputs(item.progress, index)
        this.state[index] = { ...this.state[index], ...item }
        return this.state;
    };

    getAllInputs(value, index) {
        var allInputs = document.body.querySelectorAll(".bar-input");
        var input = allInputs[index];
        var barClass = input.parentNode.className.split(" ")[1];
        var styleEl = document.head.appendChild(document.createElement("style"));
        var indicator = input.parentNode.querySelector('.bar .indicator');
        this.setBarIndicator(barClass, value, styleEl, indicator);
    };

    setBarIndicator(barClass, value, styleEl, indicatorEl) {
        styleEl.innerHTML =
            "." + barClass + " .bar-face.percentage:before {width:" + value + "%;}";
        indicatorEl.style.marginLeft = 100 + '%';
        indicatorEl.textContent = value + '%';
    }

    removeState(index) {
        let updataItem = this.cudUpdataBox.getElementsByClassName('updataItem');
        this.cudUpdataBox.removeChild(updataItem[index])
        this.state.splice(index,1)
        return this.state[index];
    };
    length() {
        return this.state.length
    }
}
export default function () {

    var _init = function () {
        var link = document.createElement("link");
        link.rel = "stylesheet";
        link.type = "text/css";
        link.href = './assets/icon/iconfont.css';
        document.getElementsByTagName("head")[0].appendChild(link);



        var link = document.createElement("link");
        link.rel = "stylesheet";
        link.type = "text/css";
        link.href = './index.css';
        document.getElementsByTagName("head")[0].appendChild(link);



        var script = document.createElement("script");
        script.src = './spark-md5.min.js';
        document.getElementsByTagName("head")[0].appendChild(script);

    }

    _init();
    /**
     * 上传函数
     * @param fileInput DOM对象
     * @param callback 回调函数
     */
    //上传文件列表对象

    //读取file内容
    var getFileContent = function (fileInput, callback) {
        if (fileInput.files && fileInput.files.length > 0 && fileInput.files[0].size > 0) {
            //下面这一句相当于JQuery的：var file =$("#upload").prop('files')[0];
            var file = fileInput.files[0];
            if (window.FileReader) {
                var reader = new FileReader();
                reader.onloadend = function (evt) {
                    if (evt.target.readyState == FileReader.DONE) {
                        callback(evt.target.result);
                    }
                };
                // 包含中文内容用gbk编码
                reader.readAsText(file, 'gbk');
            }
        }
    };





    var state = null;
    /**
     * upload内容变化时载入内容
     */
    document.getElementById('upload').onchange = async function (e) {
        flieSliceUpdata(httpPath + 'file/update', httpPath + 'file/download', httpPath + 'file/fileInfo', this.files[0])

    };
    //上传文件
    var flieSliceUpdata = async function (url, bottomUrl, urlFileInfo, file) {
        let stateIndex = 0
        let type = file.name.split(".")
        type = type[type.length - 1]
        let fileItemArry = await flieSlice(file, 100);
        //是否初始化上传文件弹窗
        if (!state) {
            state = new State({ fileName: file.name, progress: '初始化中', type: type, state: true, file: file },flieSliceUpdata)
        } else {
            stateIndex = state.addState({ fileName: file.name, progress: '初始化中', type, state: true, file: file })
        }

        //未上传文件的md5码数组
        let yetFileItemArry = [];
        //上传首段文件并查询出未上传文件有哪些
        let progress = await httpPost(url, fileItemArry[0])
        //上传文件并判断是否上传成功
        if (progress.code === 200) {
            //设置未上传文件块md5码
            yetFileItemArry = progress.data
            //判断是否有未上传文件
            if (yetFileItemArry.length !== 0) {
                //查询未上传文件并上传
                for (let index = 1; index < fileItemArry.length; index++) {
                    //只在没有未上传的块的时候才执行此方法
                    for (let yetFileItemArryIndex = 0; yetFileItemArryIndex < yetFileItemArry.length; yetFileItemArryIndex++) {
                        //查询未上传文件并上传
                        if (fileItemArry[index].get('md5') === yetFileItemArry[yetFileItemArryIndex]) {
                            //判断次项有没有暂停
                            if (state.state[stateIndex].state) {
                                let progress = await httpPost(url, fileItemArry[index])
                                if (progress.code === 200) {
                                    state.updataState(
                                        {
                                            fileName: file.name,
                                            progress: ((fileItemArry.length - yetFileItemArry.length) + index) === fileItemArry.length ? 100 :
                                                ((100 / fileItemArry.length) * ((fileItemArry.length - yetFileItemArry.length) + yetFileItemArryIndex + 1)).toFixed(2)
                                        },
                                        stateIndex,
                                        () => { bottomHttp(bottomUrl, urlFileInfo, fileItemArry[0].get('id'), file.name) })
                                }
                            } else {
                                return false
                            }
                        }
                    }

                }
            }


        } else if (progress.code === 201) {
            state.removeState(stateIndex)
            alert("此文件已经上传过！")
            return false
        } else {
            alert("服务器出现错误！")
            return false
        }

    }



    //生成文件
    function downloadFile(data, fileName) {
        if (!!window.ActiveXObject || 'ActiveXObject' in window) {
            let url = window.navigator.msSaveOrOpenBlob(new Blob(data), fileName)
        } else {
            let url = window.URL.createObjectURL(new Blob(data))
            let link = document.createElement('a')
            link.style.display = 'none'
            link.href = url
            link.setAttribute('download', fileName)
            document.body.appendChild(link)
            link.click()
            link.remove()
        }
    };

    /**
     * 下载文件
     */
    //表示未提示过是否可以使用断点续传true表示需要显示
    let indexedDBOf = true
    async function bottomHttp(url, urlFileInfo, md5, name) {
        if (!md5) {
            alert('请输入md5')
            return false
        }
        if (!name) {
            alert('请输入文件名')
            return false
        }

        let flieinfo = await httpPost(urlFileInfo, md5)

        if (flieinfo.code === 200) {
            flieinfo = flieinfo.data
        } else {
            alert('服务器出现错误！')
        }
        //解决排序
        let flieinfonum = flieinfo.length % 2 == 0 ? flieinfo.length / 2 : ((flieinfo.length - 1) / 2) + 1
        let flieinfoArry = []
        flieinfo.map((item, index) => {
            if (index < flieinfonum) {
                flieinfoArry[(index * 2) + 1] = item
            } else {
                flieinfoArry[((index - flieinfonum) * 2) + 2] = item
            }
        })
        //切片请求判断是否支持大文件缓存
        if ("indexedDB" in window) {
            let httpPostArry = []
            for (let index = 1; index < flieinfoArry.length; index++) {
                let dataBase = new indexdb(md5)
                let httpPostArryItem = await dataBase.getObject(flieinfoArry[index]['md5'])
                if (httpPostArryItem) {
                    httpPostArryItem = httpPostArryItem.file
                } else {
                    httpPostArryItem = await httpPost(url, flieinfoArry[index]['md5'], true, 'blob')
                    let dataBase = new indexdb(md5)
                    dataBase.addObject({ md5: flieinfoArry[index]['md5'], file: httpPostArryItem })
                }
                httpPostArry.push(httpPostArryItem)
            }
            let dataBase = new indexdb(md5)
            downloadFile(httpPostArry, name)
            //测试环境。正式环境请打开此代码
            //dataBase.removeObject()
        } else {
            indexedDBOf ? alert('您的浏览器不支持断点下载，大文件请注意下载进度！') : ''
            indexedDBOf = false
            let httpPostArry = []
            for (let index = 1; index < flieinfoArry.length; index++) {
                httpPostArry.push(httpPost(url, flieinfoArry[index]['md5'], true, 'blob'))
                console.log(flieinfoArry[index][md5])
            }
            Promise.all(httpPostArry).then(res => {
                downloadFile(res, name)
            })

        }
    };



    //文件切片并返回数组
    var flieSlice = async function (file, num) {
        let fileItemSize = Math.ceil(file.size / num);
        //获取MD
        let md5 = await calculate(file);
        let fileItemArry = []
        let fileItemMd5Arry = []
        for (let index = 1; index < num + 1; index++) {
            var fileItem = file.slice((fileItemSize * (index - 1)), (fileItemSize * (index)));
            let fileItemMd5Item = await calculate(new window.File([fileItem], 'calculate' + index, { type: file.type }));
            fileItemMd5Arry.push(fileItemMd5Item);
            let formdata = new FormData()
            formdata.append('content', fileItem)
            formdata.append('fileName', file.name)
            formdata.append('id', md5)
            formdata.append('index', index)
            formdata.append('length', num)
            formdata.append('md5', fileItemMd5Item)
            fileItemArry.push(formdata)
        }
        for (let index = 0; index < fileItemArry.length; index++) {
            fileItemArry[index].append('list', fileItemMd5Arry)
        }
        return fileItemArry
    }
    //获取md5
    function calculate(file) {
        return new Promise((resolved, rejected) => {
            var fileReader = new FileReader(),
                blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice,
                chunkSize = 2097152,
                // read in chunks of 2MB  
                chunks = Math.ceil(file.size / chunkSize),
                currentChunk = 0,
                spark = new SparkMD5();
            fileReader.onload = function (e) {
                spark.appendBinary(e.target.result); // append binary string  
                currentChunk++;

                if (currentChunk < chunks) {
                    loadNext();
                }
                else {
                    resolved(spark.end())
                }
            };
            function loadNext() {
                var start = currentChunk * chunkSize,
                    end = start + chunkSize >= file.size ? file.size : start + chunkSize;

                fileReader.readAsBinaryString(blobSlice.call(file, start, end));
            };

            loadNext();
        })


    }
    var httpPost = function (url, body, Header, requestHeader) {
        return new Promise((resolved, rejected) => {
            var httpRequest = new XMLHttpRequest();//第一步：建立所需的对象
            httpRequest.open('POST', url, true);//第二步：打开连接  将请求参数写在url中
            httpRequest.timeout = 5000;
            requestHeader ? httpRequest.responseType = requestHeader : ''
            Header ? httpRequest.setRequestHeader("Content-type", "application/json;charset=utf-8") : ''
            httpRequest.send(body)
            httpRequest.onload = function () {
                if (httpRequest.status === 200) {
                    resolved(requestHeader ? httpRequest.response : JSON.parse(httpRequest.responseText))
                } else {
                    rejected(false)
                }
            }
            httpRequest.ontimeout = function (e) {
                alert('网络出现错误！')
                resolved(false)
            };
        })
    }

}

